package com.yupi.project.service.impl;

import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.google.gson.reflect.TypeToken;
import com.yupi.project.service.InterfaceInvokeInfoService;
import com.yupi.project.service.UserService;
import com.yupi.yuapicommon.model.common.ErrorCode;
import com.yupi.yuapicommon.model.common.BusinessException;
import com.yupi.project.mapper.InterfaceInfoMapper;
import com.yupi.project.service.InterfaceInfoService;
import com.yupi.yuapicommon.model.dto.interfaceinfo.InvokeRequest;
import com.yupi.yuapicommon.model.entity.InterfaceInfo;
import com.yupi.yuapicommon.model.entity.InterfaceInvokeInfo;
import com.yupi.yuapicommon.model.entity.User;
import com.yupi.yuapicommon.model.enums.InterfaceInvokeInfoStatusEnum;
import com.zzsource.yuapicliensdk.client.YuApiClient;
import com.zzsource.yuapicliensdk.exception.ApiException;
import com.zzsource.yuapicliensdk.model.request.CurrencyRequest;
import com.zzsource.yuapicliensdk.model.response.ResultResponse;
import com.zzsource.yuapicliensdk.service.ApiService;
import com.zzsource.yuapicliensdk.service.impi.ApiServiceImpl;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.Nullable;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.net.MalformedURLException;
import java.net.URL;

import java.sql.Timestamp;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;

import static com.yupi.project.config.ThreadPoolConfig.COMMON_API_EXECUTOR;
import static com.yupi.yuapicommon.model.enums.InterfaceInfoStatusEnum.OFFLINE;
import static com.yupi.yuapicommon.model.enums.InterfaceInvokeInfoStatusEnum.*;

/**
* @author 12822
* @description 针对表【interface_info(接口信息)】的数据库操作Service实现
* @createDate 2023-01-06 23:51:28
*/
@Service
public class InterfaceInfoServiceImpl extends ServiceImpl<InterfaceInfoMapper, InterfaceInfo>
    implements InterfaceInfoService {

    @Resource
    private InterfaceInvokeInfoService interfaceInvokeInfoService;

    @Resource
    private UserService userService;

    @Resource
    private YuApiClient yuApiClient;

    @Resource
    @Qualifier(COMMON_API_EXECUTOR)
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;

    private final Gson gson = new Gson();

    @Override
    public void validInterfaceInfo(InterfaceInfo interfaceInfo, boolean add) {
        if (interfaceInfo == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }

        String name = interfaceInfo.getName();
        String description = interfaceInfo.getDescription();
        String method = interfaceInfo.getMethod();
        String url = interfaceInfo.getUrl();
        // 插入数据时
        if(add){
            if(StringUtils.isAnyBlank(name, method, url)){
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "参数不能为空");
            }
        }
        if (StringUtils.isNotBlank(description) && description.length() > 200L) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "描述过长");
        }
        if (StringUtils.isNotBlank(name) && name.length() > 50L) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "接口名称过长");
        }
        if (!isValidURL(url)){
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "URL不正确");
        }
        if (interfaceInfo.getWoolUsed() == null){
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "woolUsed参数不能为空");
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public InterfaceInvokeInfo invokeAsync(InvokeRequest invokeRequest, HttpServletRequest request) {
        // 获取接口信息和用户信息
        User loginUser = userService.getLoginUser(request);
        long id = invokeRequest.getId();
        InterfaceInfo interfaceInfo = this.getById(id);
        //1. 判断接口是否存在
        if(invokeRequest == null || invokeRequest.getId() <= 0){
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        if(interfaceInfo == null){
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR);
        }
        if(interfaceInfo.getStatus() == OFFLINE.getValue()){
            throw new BusinessException(ErrorCode.PARAMS_ERROR,"接口已关闭");
        }
        // 构建请求头参数
        Map<String, Object> headerParams = getStringObjectMap(invokeRequest.getHeaderParams());
        // 构建请求体参数（K-V形式）
        Map<String, Object> bodyParams = getStringObjectMap(invokeRequest.getBodyParams());
        // 构建请求路径参数（K-V形式）
        Map<String, Object> queryParams = getStringObjectMap(invokeRequest.getQueryParams());
        // 请求信息
        CurrencyRequest currencyRequest = new CurrencyRequest();
        currencyRequest.setMethod(interfaceInfo.getMethod());
        currencyRequest.setPath(interfaceInfo.getUrl());
        currencyRequest.setHeaderParams(headerParams);
        currencyRequest.setBodyParams(bodyParams);
        currencyRequest.setQueryParams(queryParams);
        // 保存接口调用信息
        InterfaceInvokeInfo interfaceInvokeInfo = new InterfaceInvokeInfo();
        interfaceInvokeInfo.setUserid(loginUser.getId());
        interfaceInvokeInfo.setInterfaceInfoid(interfaceInfo.getId());
        interfaceInvokeInfo.setRequestinfo(JSONUtil.toJsonStr(currencyRequest));
        interfaceInvokeInfo.setStatus(WAITING.getValue());
        interfaceInvokeInfo.setExecmessage(WAITING.getText());
        interfaceInvokeInfo.setCreatetime(String.valueOf(new Timestamp(System.currentTimeMillis())));
        interfaceInvokeInfo.setUpdatetime(String.valueOf(new Timestamp(System.currentTimeMillis())));
        boolean save = interfaceInvokeInfoService.save(interfaceInvokeInfo);
        if(!save){
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "数据库数据保存失败");
        }
        String accessKey = loginUser.getAccessKey();
        String secretKey = loginUser.getSecretKey();
        // 使用用户的AK/SK
        YuApiClient yuApiClient = new YuApiClient(accessKey,secretKey);
        ApiService apiService = new ApiServiceImpl();
        // 异步调用
        Long interfaceInvokeId = interfaceInvokeInfo.getId();
        threadPoolTaskExecutor.execute(() -> {
            handlerInterfaceInvokeInfoStatus(interfaceInvokeId, EXECUTING, "任务执行中");
            ResultResponse response = null;
            try {
                response = apiService.request(yuApiClient, currencyRequest);
            } catch (ApiException e) {
                log.error("接口调用异常====>", e);
                handlerInterfaceInvokeInfoStatus(interfaceInvokeId, FAIL, e.getMessage());
            }
            // 任务执行结果
            handlerInterfaceInvokeInfo(interfaceInvokeId, response);
        });
        return interfaceInvokeInfo;
    }

    @Override
    public ResultResponse invokeSync(InvokeRequest invokeRequest, HttpServletRequest request) {
        //1. 判断接口是否存在
        if(invokeRequest == null || invokeRequest.getId() <= 0){
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        long id = invokeRequest.getId();
        InterfaceInfo interfaceInfo = this.getById(id);
        if(interfaceInfo == null){
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR);
        }
        if(interfaceInfo.getStatus() == OFFLINE.getValue()){
            throw new BusinessException(ErrorCode.PARAMS_ERROR,"接口已关闭");
        }
        // 构建请求头参数
        Map<String, Object> headerParams = getStringObjectMap(invokeRequest.getHeaderParams());
        // 构建请求体参数（K-V形式）
        Map<String, Object> bodyParams = getStringObjectMap(invokeRequest.getBodyParams());
        // 构建请求路径参数（K-V形式）
        Map<String, Object> queryParams = getStringObjectMap(invokeRequest.getQueryParams());
        User loginUser = userService.getLoginUser(request);
        String accessKey = loginUser.getAccessKey();
        String secretKey = loginUser.getSecretKey();
        // 使用用户的AK/SK
        YuApiClient yuApiClient = new YuApiClient(accessKey,secretKey);
        ApiService apiService = new ApiServiceImpl();
        ResultResponse response = null;
        try {
            CurrencyRequest currencyRequest = new CurrencyRequest();
            currencyRequest.setMethod(interfaceInfo.getMethod());
            currencyRequest.setPath(interfaceInfo.getUrl());
            currencyRequest.setHeaderParams(headerParams);
            currencyRequest.setBodyParams(bodyParams);
            currencyRequest.setQueryParams(queryParams);
            // 同步调用
            response = apiService.request(yuApiClient, currencyRequest);
            handlerInvokeResult(response);
        } catch (ApiException e){
            throw new BusinessException(ErrorCode.API_REQUEST_ERROR, e.getMessage());
        }
        return response;
    }

    private void handlerInvokeResult(ResultResponse response) {
        Optional<Object> optional = Optional.ofNullable(response.getData())
                .map(r -> r.get("errorMessage"));
        if(optional.isPresent()){
            throw new BusinessException(Integer.parseInt(response.getData().get("code").toString()), response.getData().get("errorMessage").toString());
        }
    }

    /**
     * 处理接口返回结果信息
     * @param interfaceInvokeId
     * @param response
     */
    private void handlerInterfaceInvokeInfo(long interfaceInvokeId, ResultResponse response){
        InterfaceInvokeInfo updateInterfaceInvokeInfo = new InterfaceInvokeInfo();
        try{
            String jsonStr = JSONUtil.toJsonStr(response.getData());
            updateInterfaceInvokeInfo.setId(interfaceInvokeId);
            updateInterfaceInvokeInfo.setResult(jsonStr);
            interfaceInvokeInfoService.updateById(updateInterfaceInvokeInfo);
            // 请求data里是错误异常数据
            handlerInvokeResult(response);
            if(StringUtils.isBlank(jsonStr)){
                throw new BusinessException(ErrorCode.API_REQUEST_ERROR);
            }
        } catch (BusinessException be){
            handlerInterfaceInvokeInfoStatus(interfaceInvokeId, FAIL, be.getMessage());
            return;
        } catch (Exception e){
            log.error("接口调用信息处理异常===>", e);
            handlerInterfaceInvokeInfoStatus(interfaceInvokeId, FAIL, "系统异常");
            return;
        }
        handlerInterfaceInvokeInfoStatus(interfaceInvokeId, SUCCESS, "接口调用成功");
    }

    /**
     * 处理任务执行状态信息
     * @param interfaceInvokeId
     * @param interfaceInvokeInfoStatusEnum
     * @param execMessage
     */
    private void handlerInterfaceInvokeInfoStatus(long interfaceInvokeId, InterfaceInvokeInfoStatusEnum interfaceInvokeInfoStatusEnum, String execMessage){
        InterfaceInvokeInfo updateInterfaceInvokeInfo = new InterfaceInvokeInfo();
        updateInterfaceInvokeInfo.setId(interfaceInvokeId);
        updateInterfaceInvokeInfo.setStatus(interfaceInvokeInfoStatusEnum.getValue());
        updateInterfaceInvokeInfo.setExecmessage(execMessage);
        interfaceInvokeInfoService.updateById(updateInterfaceInvokeInfo);
    }

    public static boolean isValidURL(String url) {
        try {
            new URL(url);
            return true;
        } catch (MalformedURLException e) {
            return false;
        }
    }


    @Nullable
    private Map<String, Object> getStringObjectMap(List<InvokeRequest.Field> fieldList) {
        // 构建请求参数
        String requestParams = "{}";
        if (fieldList != null && fieldList.size() > 0) {
            JsonObject jsonObject = new JsonObject();
            for (InvokeRequest.Field field : fieldList) {
                jsonObject.addProperty(field.getFieldName(), field.getValue());
            }
            requestParams = gson.toJson(jsonObject);
        }
        Map<String, Object> params = new Gson().fromJson(requestParams, new TypeToken<Map<String, Object>>() {
        }.getType());
        return params;
    }
}




